//
//	(c) 2009 Andrew Kalmacky
//
#include "types.h"

using namespace ak::dom;

const char_t* const ak::dom::log::prefix = T_("[dom]");

static void check(bool condition)
{
	log::info::assert(condition, T_("type check error"));
}

//
// --- IntType
//
IntType::IntType(const pin<TypeName>& name)
	: Type(name)
{
}

TypeId IntType::get_type_id() const
{
	return TYPE_INT;
}
bool IntType::is_int_signed() const
{
	return true;
}
void IntType::destruct(void* ptr) const
{
}

//
// --- I8Type
//
I8Type::I8Type(const pin<TypeName>& name)
	: IntType(name)
{
}
size_t I8Type::get_instance_size() const
{
	return 1;
}
int I8Type::read_int(void* ptr) const
{
	return *(int8_t*) ptr;
}
long64 I8Type::read_long64(void* ptr) const
{
	return *(int8_t*) ptr;
}
void I8Type::write_int(void* ptr, int val) const
{
	check(val >= -128 && val <= 127);
	*(int8_t*)ptr = int8_t(val);
}
void I8Type::write_long64(void* ptr, long64 val) const
{
	check(val >= -128 && val <= 127);
	*(int8_t*)ptr = int8_t(val);
}
void I8Type::construct(void* ptr) const
{
	*(int8_t*)ptr = 0;
}
void I8Type::copy(void* dst_ptr, void* src_ptr) const
{
	*(int8_t*)dst_ptr = *(int8_t*)src_ptr;
}

//
// --- U8Type
//
U8Type::U8Type(const pin<TypeName>& name)
	: I8Type(name)
{
}

int U8Type::read_int(void* ptr) const
{
	return *(uint8_t*) ptr;
}

long64 U8Type::read_long64(void* ptr) const
{
	return *(uint8_t*) ptr;
}

void U8Type::write_int(void* ptr, int val) const
{
	check((unsigned int)val <= 255);
	*(uint8_t*)ptr = uint8_t(val);
}

void U8Type::write_long64(void* ptr, long64 val) const
{
	check((uint64_t) val <= 255);
	*(uint8_t*)ptr = uint8_t(val);
}

bool U8Type::is_int_signed() const
{
	return false;
}

//
// --- I16Type
//
I16Type::I16Type(const pin<TypeName>& name)
	: IntType(name)
{
}

size_t I16Type::get_instance_size() const
{
	return 2;
}

int I16Type::read_int(void* ptr) const
{
	return *(int16_t*) ptr;
}

long64 I16Type::read_long64(void* ptr) const
{
	return *(int16_t*) ptr;
}

void I16Type::write_int(void* ptr, int val) const
{
	check(val >= -32768 && val <= 32767);
	*(int16_t*)ptr = int16_t(val);
}

void I16Type::write_long64(void* ptr, long64 val) const
{
	check(val >= -32768 && val <= 32767);
	*(int16_t*)ptr = int16_t(val);
}

void I16Type::construct(void* ptr) const
{
	*(int16_t*)ptr = 0;
}

void I16Type::copy(void* dst_ptr, void* src_ptr) const
{
	*(int16_t*)dst_ptr = *(int16_t*)src_ptr;
}

//
// --- U16Type
//
U16Type::U16Type(const pin<TypeName>& name)
	: I16Type(name)
{
}

int U16Type::read_int(void* ptr) const
{
	return *(uint16_t*) ptr;
}

long64 U16Type::read_long64(void* ptr) const
{
	return *(uint16_t*) ptr;
}

void U16Type::write_int(void* ptr, int val) const
{
	check((unsigned int)val <= 65535);
	*(uint16_t*)ptr = uint16_t(val);
}

void U16Type::write_long64(void* ptr, long64 val) const
{
	check((uint64_t) val <= 65535);
	*(uint16_t*)ptr = uint16_t(val);
}

bool U16Type::is_int_signed() const
{
	return false;
}

//
// --- I32Type
//
I32Type::I32Type(const pin<TypeName>& name)
	: IntType(name)
{
}

size_t I32Type::get_instance_size() const
{
	return 4;
}

int I32Type::read_int(void* ptr) const
{
	return *(int32_t*) ptr;
}

long64 I32Type::read_long64(void* ptr) const
{
	return *(int32_t*) ptr;
}

void I32Type::write_int(void* ptr, int val) const
{
	*(int32_t*)ptr = val;
}

void I32Type::write_long64(void* ptr, long64 val) const
{
	check(val >= -0x7fffffff && val <= 0x7fffffff);
	*(int32_t*)ptr = int32_t(val);
}

void I32Type::construct(void* ptr) const
{
	*(int16_t*)ptr = 0;
}

void I32Type::copy(void* dst_ptr, void* src_ptr) const
{
	*(int16_t*)dst_ptr = *(int16_t*)src_ptr;
}

//
// --- U32Type
//
U32Type::U32Type(const pin<TypeName>& name)
	: I32Type(name)
{
}

int U32Type::read_int(void* ptr) const
{
	return *(uint32_t*) ptr;
}

long64 U32Type::read_long64(void* ptr) const
{
	return *(uint32_t*) ptr;
}

void U32Type::write_int(void* ptr, int val) const
{
	*(uint32_t*)ptr = val;
}

void U32Type::write_long64(void* ptr, long64 val) const
{
	check((uint32_t) val <= 0xffffffff);
	*(uint32_t*)ptr = uint32_t(val);
}
bool U32Type::is_int_signed() const
{
	return false;
}

//
// --- I64Type
//
I64Type::I64Type(const pin<TypeName>& name)
	: IntType(name)
{
}

size_t I64Type::get_instance_size() const
{
	return 8;
}

int I64Type::read_int(void* ptr) const
{
	int64_t r = *(int64_t*)ptr;
	check(r >= -0x7fffffff && r <= 0x7fffffff);
	return int32_t(r);
}

long64 I64Type::read_long64(void* ptr) const
{
	return *(int64_t*)ptr;
}

void I64Type::write_int(void* ptr, int val) const
{
	*(int64_t*)ptr = val;
}

void I64Type::write_long64(void* ptr, long64 val) const
{
	*(int64_t*)ptr = val;
}

void I64Type::construct(void* ptr) const
{
	*(int64_t*)ptr = 0;
}

void I64Type::copy(void* dst_ptr, void* src_ptr) const
{
	*(int64_t*)dst_ptr = *(int64_t*)src_ptr;
}

//
// --- U64Type
//
U64Type::U64Type(const pin<TypeName>& name)
	: I64Type(name)
{
}

int U64Type::read_int(void* ptr) const
{
	uint64_t r = *(uint64_t*) ptr;
	check(r <= 0xffffffff);
	return int32_t(r);
}

long64 U64Type::read_long64(void* ptr) const
{
	return *(int64_t*) ptr;
}

void U64Type::write_int(void* ptr, int val) const
{
	*(uint64_t*)ptr = val;
}

void U64Type::write_long64(void* ptr, long64 val) const
{
	*(uint64_t*)ptr = val;
}

bool U64Type::is_int_signed() const
{
	return false;
}

//
// --- FloatType
//
FloatType::FloatType(const pin<TypeName>& name)
	: Type(name)
{
}

TypeId FloatType::get_type_id() const
{
	return TYPE_FLOAT;
}

size_t FloatType::get_instance_size() const
{
	return 32;
}

size_t FloatType::get_float_m_width() const
{
	return 24;
}

size_t FloatType::get_float_p_width() const
{
	return 7;
}

double FloatType::read_double(void* ptr) const
{
	return *(float*)ptr;
}

void FloatType::write_double(void* ptr, double val) const
{
	check(abs(float(val) - val) <= std::numeric_limits<float>::epsilon());
	*(float*) ptr = float(val);
}

void FloatType::construct(void* ptr) const
{
	*(float*) ptr = 0;
}

void FloatType::destruct(void* ptr) const
{
}

void FloatType::copy(void* dst_ptr, void* src_ptr) const
{
	*(float*)dst_ptr = *(float*)src_ptr;
}

//
// --- DoubleType
//
DoubleType::DoubleType(const pin<TypeName>& name)
	: Type(name)
{
}

TypeId DoubleType::get_type_id() const
{
	return TYPE_FLOAT;
}

size_t DoubleType::get_instance_size() const
{
	return 64;
}

size_t DoubleType::get_float_m_width() const
{
	return 48;
}

size_t DoubleType::get_float_p_width() const
{
	return 15;
}

double DoubleType::read_double(void* ptr) const
{
	return *(double*)ptr;
}

void DoubleType::write_double(void* ptr, double val) const
{
	*(double*) ptr = val;
}

void DoubleType::construct(void* ptr) const
{
	*(double*) ptr = 0;
}

void DoubleType::destruct(void* ptr) const
{
}

void DoubleType::copy(void* dst_ptr, void* src_ptr) const
{
	*(double*)dst_ptr = *(double*)src_ptr;
}

//
// --- EnumType
//
EnumType::EnumType(const pin<TypeName>& name, const pin<Type>& base_type, const std::list<string>& tags)
	: Type(name)
	, base_type(base_type)
{
	for (std::list<string>::const_iterator i = tags.begin(); i != tags.end(); ++i)
		this->tags.push_back(new EnumTag(*i, this->tags.size()));
}

TypeId EnumType::get_type_id() const
{
	return TYPE_ENUM;
}

size_t EnumType::get_instance_size() const
{
	return base_type->get_instance_size();
}

pin<EnumTag> EnumType::read_tag(void* ptr) const
{
	size_t i = size_t(base_type->read_int(ptr));
	check(i < tags.size());
	return tags[i];
}
void EnumType::write_tag(void* ptr, const pin<EnumTag>& val) const
{
	size_t val_index = val->get_val();
	check(tags.size() < val_index && tags[val_index] == val);
	base_type->write_int(ptr, val_index);
}
size_t EnumType::get_enum_tags_count() const
{
	return tags.size();
}

pin<EnumTag> EnumType::get_enum_tag(size_t index) const
{
	return tags[index];
}
void EnumType::construct(void* ptr) const
{
	base_type->construct(ptr);
}

void EnumType::destruct(void* ptr) const
{
	base_type->destruct(ptr);
}

void EnumType::copy(void* dst_ptr, void* src_ptr) const
{
	base_type->copy(src_ptr, dst_ptr);
}

//
// --- StringType
//
StringType::StringType(const pin<TypeName>& name)
	: Type(name)
{
}

TypeId StringType::get_type_id() const
{
	return TYPE_STRING;
}

size_t StringType::get_instance_size() const
{
	return sizeof string;
}

string StringType::read_string(void* ptr) const
{
	return *(string*)ptr;
}

void StringType::write_string(void* ptr, const string& val) const
{
	*(string*)ptr = val;
}

void StringType::construct(void* ptr) const
{
	new(ptr) string;
}

void StringType::destruct(void* ptr) const
{
	((string*)ptr)->string::~string();
}

void StringType::copy(void* dst_ptr, void* src_ptr) const
{
	new(dst_ptr) string(*(string*)src_ptr);
}

//
// --- MasterPtrType
//
MasterPtrType::MasterPtrType(const pin<TypeName>& name)
	: Type(name)
{
}

TypeId MasterPtrType::get_type_id() const
{
	return TYPE_PTR;
}

size_t MasterPtrType::get_instance_size() const
{
	return sizeof master<Node>;
}

void MasterPtrType::construct(void* ptr) const
{
	new(ptr) master<Node>;
}

void MasterPtrType::destruct(void* ptr) const
{
	((master<Node>*)ptr)->master<Node>::~master();
}

void MasterPtrType::copy(void* dst_ptr, void* src_ptr) const
{
	new(dst_ptr) master<Node>(*(master<Node>*)src_ptr);
}

pin<Node> MasterPtrType::read_ptr(void* ptr) const
{
	return *(master<Node>*)ptr;
}

void MasterPtrType::write_ptr(void* ptr, const pin<Node>& val) const
{
	*(master<Node>*)ptr = val;
}

//
// --- WeakPtrType
//
WeakPtrType::WeakPtrType(const pin<TypeName>& name)
	: Type(name)
{
}

TypeId WeakPtrType::get_type_id() const
{
	return TYPE_PTR;
}

size_t WeakPtrType::get_instance_size() const
{
	return sizeof weak<Node>;
}

void WeakPtrType::construct(void* ptr) const
{
	new(ptr) weak<Node>;
}

void WeakPtrType::destruct(void* ptr) const
{
	((weak<Node>*)ptr)->weak<Node>::~weak();
}

void WeakPtrType::copy(void* dst_ptr, void* src_ptr) const
{
	new(dst_ptr) weak<Node>(*(weak<Node>*)src_ptr);
}

pin<Node> WeakPtrType::read_ptr(void* ptr) const
{
	return *(weak<Node>*)ptr;
}

void WeakPtrType::write_ptr(void* ptr, const pin<Node>& val) const
{
	*(weak<Node>*)ptr = val;
}

//
// --- StaticArrayType
//
StaticArrayType::StaticArrayType(const pin<TypeName>& name, const pin<Type>& items_type, size_t size)
	: Type(name)
	, items_type(items_type)
	, size(size)
{
}

size_t StaticArrayType::get_instance_size() const
{
	return items_type->get_instance_size() * size;
}

TypeId StaticArrayType::get_type_id() const
{
	return TYPE_VECTOR;
}

size_t StaticArrayType::get_vector_items_count() const
{
	return size;
}

pin<Type> StaticArrayType::get_vector_items_type() const
{
	return items_type;
}

void StaticArrayType::construct(void* ptr) const
{
	char* dst = reinterpret_cast<char*>(ptr);
	size_t step = items_type->get_instance_size();
	for (size_t i = 0; i < size; i++, dst += step)
		items_type->construct(dst);
}

void StaticArrayType::destruct(void* ptr) const
{
	char* dst = reinterpret_cast<char*>(ptr);
	size_t step = items_type->get_instance_size();
	for (size_t i = 0; i < size; i++, dst += step)
		items_type->destruct(dst);
}

void StaticArrayType::copy(void* dst_ptr, void* src_ptr) const
{
	char* src = reinterpret_cast<char*>(src_ptr);
	char* dst = reinterpret_cast<char*>(dst_ptr);
	size_t step = items_type->get_instance_size();
	for (size_t i = 0; i < size; i++, dst += step, src += step)
		items_type->copy(dst, src);
}

size_t StaticArrayType::read_items_count(void* ptr) const
{
	return size;
}

void* StaticArrayType::read_item_offset(void* ptr, size_t index) const
{
	check(index < size);
	return reinterpret_cast<char*>(ptr) + (index * items_type->get_instance_size());
}

//
// --- 
//
